﻿#region

using System;
using System.Collections.Generic;
using System.Linq;
using Tateeda.ClinicalStudy.Base.Data.DBModel;
using Tateeda.ClinicalStudy.Base.Data.EF;
using Tateeda.ClinicalStudy.Base.Modules.Common.Enumeration;
using Tateeda.ClinicalStudy.Base.Modules.Common.Extenstions;
using Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Interfaces.Services;
using Tateeda.ClinicalStudy.Base.Modules.Models.Medication;
using Tateeda.ClinicalStudy.Base.Modules.Models.Study;
using Tateeda.ClinicalStudy.Base.Modules.Models.StudyForm;
using Tateeda.ClinicalStudy.Base.Modules.Models.StudySite;
using Tateeda.ClinicalStudy.Base.Modules.Models.StudyVisit;
using Tateeda.ClinicalStudy.Base.Modules.Models.User;

#endregion

#region - Legal -

///<legal>
/// Copyright © 2011, Tateeda Media Network
/// All rights reserved. http://tateeda.com
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions
/// are met:
///
/// - Redistributions of source code must retain the above copyright
/// notice, this list of conditions and the following disclaimer.
///
/// - Neither the name of the Tateeda Media Network, nor the names of its
/// contributors may be used to endorse or promote products
/// derived from this software without specific prior written
/// permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
/// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
/// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
/// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
/// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
/// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
/// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
/// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
/// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///</legal>

#endregion - Legal -

namespace Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Concrete.Services {

    public class SubjectService : ISubjectService {
        private readonly IUnitOfWork _unitOfWork;

        public SubjectService() {
            _unitOfWork = new SqlUnitOfWork();
        }

        #region - Incaplsulate other services calles here -

        public virtual IUnitOfWork UnitOfWork {
            get { return _unitOfWork; }
        }

        public virtual void AddAlternativeNumber(SubjectAltNumberModel altNumModel) {
            SubjectAltNumber altNum = altNumModel.ToSubjectAltNumber();
            UnitOfWork.SubjectAltNumbers.AddObject(altNum);
            UnitOfWork.Commit();
        }

        public virtual DateTime ConvertTimeFromUTC(int appUserId, DateTime universalTime) {
            IApplicationService appService = new ApplicationService();
            return appService.ConvertTimeFromUTC(appUserId, universalTime);
        }

        public virtual DateTime ConvertTimeToUTC(int appUserId, DateTime sourceTime) {
            IApplicationService appService = new ApplicationService();
            return appService.ConvertTimeToUTC(appUserId, sourceTime);
        }

        public virtual AppointmentModel CreateApointment(int subjectId, AppointmentModel appointment) {
            if (appointment == null) throw new ArgumentNullException("appointment");
            var firstAppt = appointment.ToAppointment();
            firstAppt.CreatedOn = DateTime.UtcNow;
            firstAppt.UpdatedOn = DateTime.UtcNow;
            firstAppt.SubjectId = subjectId;
            UnitOfWork.Appointments.AddObject(firstAppt);
            UnitOfWork.Commit();
            return firstAppt.ToAppointmentModel();
        }

        public void CreateApointmentWithSequence(int subjectId, ICollection<AppointmentModel> folowwingSequece) {
            foreach (var appt in folowwingSequece) {
                var a = new Appointment {
                    VisitId = appt.VisitId,
                    SubjectId = subjectId,
                    StartTime = appt.StartTime,
                    EndTime = appt.EndTime,
                    Status = StudyStatus.NotStartedStatus.ToStatusInt(),
                    CreatedOn = DateTime.UtcNow,
                    UpdatedOn = DateTime.UtcNow,
                    AppUserId = appt.AppUserId
                };
                UnitOfWork.Appointments.AddObject(a);
            }
            UnitOfWork.Commit();
        }

        public virtual SubjectModel CreateSubject(SubjectModel subject) {
            decimal? result =
                UnitOfWork.Context.uspSubjectInsert(subject.SiteId, subject.Nickname, subject.FamilyId, subject.Notes,
                                                    subject.NIMHID, subject.Gender, subject.YearBorn, subject.Status,
                                                    subject.SortOrder, subject.FirstName, subject.LastName,
                                                    subject.DateOfBirth).FirstOrDefault();

            if (result != null) return GetSubject((int)result.Value);
            return null;
        }

        public virtual void DeleteAlternativeNumber(int subjectAltNumberId) {
            SubjectAltNumber altNum =
                UnitOfWork.SubjectAltNumbers.Where(alt => alt.SubjectAltNumberId == subjectAltNumberId).First();
            UnitOfWork.SubjectAltNumbers.DeleteObject(altNum);
            UnitOfWork.Commit();
        }

        public virtual ICollection<SubjectModel> FindSubjects(int siteId, string firstName, string lastName) {
            ICollection<Subject> list = UnitOfWork.Context.uspSubjectFindByFirstLastName(siteId, firstName, lastName).ToList();
            ICollection<Subject> subjList = list.Where(s => s.SiteId == siteId).ToList();

            return subjList.Select(s => s.ToSubjectModel()).ToList();
        }

        public virtual ICollection<StudyFormModel> GetAppointmentForms(int appointmentId, int? status) {
            IAppointmentService apptService = new AppointmentService();
            if (!status.HasValue) {
                return apptService.GetAppointmentFormsAll(appointmentId);
            }
            return apptService.GetAppointmentFormsForStatus(appointmentId, status.Value);
        }

        public virtual AppUserModel GetAppUserByLoginName(string userName) {
            IUserService userService = new UserService();
            return userService.FindUserByName(userName);
        }

        public virtual ICollection<SubjectModel> GetAppUserSubjects(int appUserId) {
            IQueryable<Subject> subs = from ap in UnitOfWork.Appointments
                                       join s in UnitOfWork.Subjects
                                           on ap.SubjectId equals s.SubjectId
                                       where ap.AppUserId == appUserId
                                       select s;
            return subs.ToList().ToSubjectModelList();
        }

        public virtual VisitModel GetFirstVisit() {
            IStudyService stusdyService = new StudyService();
            return stusdyService.GetFirstVisit();
        }

        public virtual SiteModel GetSite(int siteId) {
            ISiteService siteService = new SiteService();
            return siteService.GetSite(siteId);
        }

        public virtual ICollection<SubjectModel> GetSiteSubjects(int siteId) {
            return
                UnitOfWork.Subjects.Where(sub => sub.SiteId == siteId && sub.Status == (int)StudyStatus.ActiveStatus).
                    ToList().ToSubjectModelList();
        }

        public virtual SubjectModel GetSubject(int subjectId) {
            var subj = UnitOfWork.Context.uspGetSubjectById(subjectId).FirstOrDefault();
            var model = subj.ToSubjectModel();
            model.Appointments = UnitOfWork.Appointments.Where(appt => appt.SubjectId == subjectId).ToList();
            model.RecreationalDrugOrSubstances =
                UnitOfWork.RecreationalDrugOrSubstances.Where(sub => sub.SubjectId == subjectId).ToList();
            model.SubjectDrugs = UnitOfWork.SubjectDrugs.Where(dr => dr.SubjectId == subjectId).ToList();
            return model;
        }

        public virtual ICollection<SubjectAltNumberModel> GetSubjectAlternativeNumbers(int subjectId) {
            var query =
                UnitOfWork.SubjectAltNumbers.Where(alt => alt.SubjectId == subjectId).ToList();
            return query.Select(num => num.ToSubjectAltNumberModel()).ToList();
        }

        public virtual ICollection<SubjectDrugModel> GetSubjectDrugPerAppointment(int appointmentId, bool isHistoryDrug = false) {
            var drugs = UnitOfWork.SubjectDrugs.Where(sd => sd.AppointmentId == appointmentId && sd.IsBeforeStudy).ToList();
            return drugs.Select(d => d.ToSubjectDrugModel()).ToList();
        }

        public virtual ICollection<SubjectDrugModel> GetSubjectDrugs(int subjectId) {
            var drugs = UnitOfWork.SubjectDrugs.Where(sd => sd.SubjectId == subjectId).ToList();
            return drugs.Select(d => d.ToSubjectDrugModel()).ToList();
        }

        public virtual ICollection<RecreationalDrugOrSubstanceModel> GetSubjectSubstancesAll(int subjectId) {
            var subs =
                UnitOfWork.RecreationalDrugOrSubstances.Where(s => s.SubjectId == subjectId).ToList();
            return subs.ToSubstanceModelList();
        }

        public virtual ICollection<RecreationalDrugOrSubstanceModel> GetSubjectSubstancesPerAppointment(int subjectId, int appointmentId) {
            var subs = UnitOfWork.RecreationalDrugOrSubstances.Where(
                    s => s.SubjectId == subjectId && s.AppointementId == appointmentId).ToList();
            return subs.ToSubstanceModelList();
        }

        public void MapAvailableSubjectVisits(int subjectId, ICollection<int> visitMap) {
            var subject = UnitOfWork.Subjects.Where(s => s.SubjectId == subjectId).SingleOrDefault();

            //Clear first all the mapping
            ClearSubjectAvailableVisitMap(subjectId);

            foreach (var item in visitMap) {
                int id = item;
                var visit = UnitOfWork.Visits.Where(v => v.VisitId == id).SingleOrDefault();
                if (subject != null) subject.Visits.Add(visit);
            }
            UnitOfWork.Commit();
        }

        public void ClearSubjectAvailableVisitMap(int subjetId) {
            var visitList = UnitOfWork.Visits.Where(v => v.Subjects.Any(s => s.SubjectId == subjetId));
            foreach (var visit in visitList) {
                UnitOfWork.Visits.DeleteObject(visit);
            }
            UnitOfWork.Commit();
        }

        public virtual void UpdateAlternativeNumber(SubjectAltNumberModel altNum) {
            var altNumOld = UnitOfWork.SubjectAltNumbers.Where(num => num.SubjectAltNumberId == altNum.SubjectAltNumberId).First();
            altNumOld.AltId = altNum.AltId;
            altNumOld.Description = altNum.Description;
            altNumOld.Comment = altNum.Comment;
            altNumOld.UpdatedOn = DateTime.UtcNow;
            UnitOfWork.Commit();
        }

        public virtual void UpdateSubject(SubjectModel subject) {
            UnitOfWork.Context.uspSubjectUpdate(subject.SubjectId, subject.SiteId, subject.Nickname, subject.FamilyId,
                                                subject.Notes, subject.NIMHID, subject.Gender, subject.YearBorn,
                                                subject.Status, subject.SortOrder, subject.FirstName, subject.LastName,
                                                subject.DateOfBirth);
        }

        public ICollection<VisitStepVisitSequenceModel> GetVisitStepVisitsSequence(int stepId) {
            var sequence = UnitOfWork.VisitStepVisitSequences.Where(vs => vs.VisitStepId == stepId).ToList();
            return sequence.ToVisitStepVisitSequenceModelList();
        }

        public ICollection<int> GetVisitStepAvailableVisitsMap(int stepId) {
            var visits = UnitOfWork.Visits.Where(v => v.VisitSteps.Any(s => s.VisitStepId == stepId && s.IsActive)).ToList();
            return visits.Select(v => v.VisitId).ToList();
        }

        #endregion
    }
}